/*
 * Copyright (c) 2024 Black Sesame Technologies
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <csignal>
#include <thread>
#include <iostream>
#include <unistd.h>
#include <unordered_map>
#include <typeinfo>
#include "dal/identifier.h"
#include "dal/domain_participant.h"
#include "dal/topic.h"
#include "dal/publisher.h"
#include "dal/subscriber.h"
#include "dal/config_loader.h"
#include "my_package/msg/all_type.hpp"

volatile sig_atomic_t g_signal_received = false;

void signal_handler(int signal) {
    g_signal_received = true;
}

static void reg_sigs()
{
    std::signal(SIGINT, signal_handler);
    std::signal(SIGILL, signal_handler);
    std::signal(SIGABRT, signal_handler);
    std::signal(SIGSEGV, signal_handler);
    std::signal(SIGTERM, signal_handler);
    std::signal(SIGBUS, signal_handler);
}

void on_data_available_callback(dal_datareader_t* reader, void* data)
{
    dal_sample_info_t info;
    my_package::msg::AllType msg;

    if (dal_datareader_take_next_sample(reader, &msg, &info) == DAL_RETCODE_OK) {
        if (info.valid_data == 1) {
            std::cout << "[lisener]: recv msg, i16: " << msg.i16 << ", str: " << msg.str
                      << ", unbound_u8_arr's size: " << msg.unbound_u8_arr.size() << std::endl;
        }
    } else {
        std::cout << "[lisener]: recv msg failed" << std::endl;
    }
}

int main(int argc, char* argv[])
{
#if defined (__x86_64__)
    std::string prefix = "/opt/cyber/usr/bin/dal/TestCases/Helloworld/conf/";
#elif defined (__android_aarch64__)
    std::string prefix = "/vendor/etc/dal/conf/";
#else
    std::string prefix = "/usr/bin/autoplt/demos/bst-hanhai-dal-demo/HelloWorld/conf/";
#endif
    std::string fastdds_conf_path = prefix + "dal_fastdds_profiles.xml";
    std::string cyclonedds_conf_path = prefix + "dal_cyclonedds_config.xml";
    std::string rtidds_conf_path = prefix + "DAL_RTI_USER_QOS_PROFILES.xml";

    std::unordered_map<std::string, const char*> conf_file_path_map = {
        {DAL_FASTDDS_IDENTIFIER, fastdds_conf_path.c_str()},
        {DAL_CYCLONEDDS_IDENTIFIER, cyclonedds_conf_path.c_str()},
        {DAL_RTICONNEXTDDS_IDENTIFIER, rtidds_conf_path.c_str()}
    };

    reg_sigs();

    dal_load_config_from_path(conf_file_path_map.at(dal_get_identifier()), nullptr);
    // create participant
    dal_domain_participant_t* part =
        dal_create_domain_participant(20, DAL_PARTICIPANT_QOS_DEFAULT, NULL, 0);

    // create publisher
    dal_publisher_t* pub =
        dal_domain_participant_create_publisher(part, DAL_PUBLISHER_QOS_DEFAULT, NULL, 0);

    // create subscriber
    dal_subscriber_t* sub =
        dal_domain_participant_create_subscriber(part, DAL_SUBSCRIBER_QOS_DEFAULT, NULL, 0);

    // register type
    dal_type_support_t* ts =
        dal_create_type_support((void*)(my_package::msg::AllType::FullyQualifiedName()));
    dal_type_support_register_type(ts, part, dal_type_support_get_type_name(ts));

    // create topic
    dal_topic_t* topic = dal_domain_participant_create_topic(part,
                                                             "HelloWorldTopic",
                                                             dal_type_support_get_type_name(ts),
                                                             DAL_TOPIC_QOS_DEFAULT,
                                                             nullptr,
                                                             0);

    // create data writer
    dal_datawriter_qos_t* writer_qos = dal_create_default_datawriter_qos();
    writer_qos->durability.kind = DAL_TRANSIENT_LOCAL_DURABILITY_QOS;
    writer_qos->history.kind = DAL_KEEP_ALL_HISTORY_QOS;
    writer_qos->history.depth = 10;
    writer_qos->reliability.kind = DAL_RELIABLE_RELIABILITY_QOS;
    writer_qos->reliability.max_blocking_time = {10, 0};

    dal_datawriter_t* writer =
        dal_publisher_create_datawriter(pub, topic, writer_qos, NULL, DAL_ANY_STATUS);

    // create data reader
    dal_datareader_listener_callbacks_t reader_callbacks = { };
    reader_callbacks.on_data_available = &on_data_available_callback;
    dal_datareader_listener_t* reader_listener = dal_create_datareader_listener(reader_callbacks);

    dal_datareader_qos_t* reader_qos = dal_create_default_datareader_qos();
    reader_qos->durability.kind = DAL_TRANSIENT_LOCAL_DURABILITY_QOS;
    reader_qos->history.kind = DAL_KEEP_ALL_HISTORY_QOS;
    reader_qos->history.depth = 10;
    reader_qos->reliability.kind = DAL_RELIABLE_RELIABILITY_QOS;
    reader_qos->reliability.max_blocking_time = {10, 0};

    dal_datareader_t* reader =
        dal_subscriber_create_datareader(sub, topic, reader_qos, reader_listener, DAL_ANY_STATUS);

    int data_size = 1024;
    if (argc >= 2) {
        data_size = atof(argv[1]);
    }

    double speed = 1;
    if (argc >= 3) {
        speed = atof(argv[2]);
    }

    my_package::msg::AllType msg;
    msg.str = "AllType";
    msg.unbound_u8_arr = std::vector<uint8_t>(data_size, 8);
    msg.under.name = "AllTypeUnder";
    msg.under.un_int_arr = std::vector<int32_t>(1, 8);

    while (!g_signal_received) {
        auto ret = dal_datawriter_write(writer, &msg, {0, 0, 0, 0});
        std::cout << "[talker]: send msg, i16: " << msg.i16 << ", str:" << msg.str
                  << ", unbound_u8_arr's size: " << msg.unbound_u8_arr.size() << std::endl;
        msg.i16 += 1;
        std::this_thread::sleep_for(std::chrono::milliseconds(int(1000 / speed)));
    }

    dal_delete_datawriter_qos(writer_qos);
    dal_delete_datareader_qos(reader_qos);
    dal_publisher_delete_datawriter(pub, writer);
    dal_subscriber_delete_datareader(sub, reader);
    dal_delete_datareader_listener(reader_listener);
    dal_domain_participant_delete_topic(part, topic);
    dal_delete_type_support(ts);
    dal_domain_participant_delete_publisher(part, pub);
    dal_domain_participant_delete_subscriber(part, sub);
    dal_delete_domain_participant(part);
}